from PyQt5.QtGui import QPainter, QPaintEvent, QColor, QPen, QMouseEvent
from PyQt5.QtWidgets import QWidget, QMenu, QAction
from PyQt5.QtCore import Qt, QRectF, QPointF, QPoint
from GUI.Widget.ControllerTag import ControllerTag

import math
import numpy as np
import Utility.CubicSpline as CubicSpline


class HistogramCanvasWidget(QWidget):
    histogramData: np.ndarray = None  # 灰度直方图数据,除以最大值归一化后，1xn 的数组
    color = QColor(255, 255, 255, 255)  # 画笔颜色

    def __init__(self, parent):
        super().__init__(parent)
        self.initUI()

    def initUI(self):
        # self.setStyleSheet("background-color: #0000FF;")
        # self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowFlags(Qt.FramelessWindowHint)

    def paintEvent(self, event: QPaintEvent):
        if self.histogramData is None:
            return
        data = self.histogramData
        color = self.color
        w, h = self.width(), self.height()
        padding = (0, 0, 0, 0)  # 上下左右
        w2 = w - padding[2] - padding[3]
        h2 = h - padding[0] - padding[1]
        # 绘制 直方图
        col = self.histogramData.shape[0]  # 数据列数
        spin = w2 / 256 * math.ceil(256 / col)  # 界面步进
        height = h2  # 直方图 最大高度

        qp = QPainter()
        qp.begin(self)
        for i in range(0, col - 1):
            rect = QRectF(padding[2] + i * spin, padding[0] + (1 - data[i]) * height, spin,
                          data[i] * height)  # 左上角x,yRate w,h
            qp.fillRect(rect, color)
        # pen = QPen(Qt.blue, 5, Qt.SolidLine)
        # qp.setPen(pen)
        # 绘制最后一个数据项
        i = col - 1
        spin = w / 256 * (256 % col)  # 步进+
        rect = QRectF(w2 + padding[2] - spin, padding[0] + (1 - data[i]) * height, spin, data[i] * height)  # 左上角x,yRate w,h
        qp.fillRect(rect, self.color)
        qp.end()


class Tag(ControllerTag):
    def __init__(self, parent, controller=None):
        super().__init__(parent, controller)
        self.initUI()

    def initUI(self):
        self.resize(8, 8)
        self.show()

    def paintEvent(self, event: QPaintEvent):
        # 绘制圆形
        qp = QPainter()
        qp.begin(self)
        qp.fillRect(self.rect(), Qt.blue)
        qp.end()

    def mouseReleaseEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            print('tag: 释放鼠标左键')
            self.Parent.Active = False
            self.Parent.OperatingTag = None
            self.Parent.updateSlot()
            event.accept()


class CurveCanvasWidget(QWidget):
    histogramData = None  # 灰度直方图数据,除以最大值归一化后，1xn 的数组
    color = (QColor(200, 200, 200), QColor(220, 20, 60), QColor(60, 179, 113), QColor(0, 191, 255))  # 画笔颜色
    channel = 0  # 当前通道
    tagList = None  # tag列表 (全部RGB) 四个方法
    curveDataList = [None, None ,None ,None]  # 灰度级映射数据 曲线(全,R,G,B)
    Active = False  # 为true时更新曲线数据
    OperatingTag = None  # 正在操作的tag
    moveCnt = 0  # 移动计数,每移动三次计算一次数据

    # 部件
    context = None
    dialog = None  # 主对话框
    contextTag = None   # 当前context目标tag
    contextPos = None # 当前context点

    def __init__(self, parent):
        super().__init__(parent)
        self.tagList = [[], [], [], []]  # tag列表 (全部RGB) 四个方法
        self.curveDataList = [None, None, None, None]  # 灰度级映射数据 曲线(全,R,G,B)
        self.moveCnt = 0  # 移动计数,每移动三次计算一次数据
        # 部件
        self.context = None
        self.dialog = None  # 主对话框
        self.contextTag = None  # 当前context目标tag
        self.contextPos = None  # 当前context点
        self.initUI()

    def initUI(self):
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.initTag()
        self.initContext()

    def initContext(self):
        # Content
        menu = QMenu(self)
        self.context = menu

        Act = QAction("新建", menu)
        menu.addAction(Act)
        Act.triggered.connect(self.__newTagAction)
        menu.newTagAct = Act

        Act = QAction("删除", menu)
        menu.addAction(Act)
        Act.triggered.connect(self.__delTagAction)
        menu.delTagAct = Act

    def newTag(self, x, y):
        tag = Tag(self, self)
        tag.move2(x, y)
        return tag

    def __newTagAction(self):
        if self.contextPos:
            x = self.contextPos.x()
            # 寻找合适的列表位置
            tags = self.tagList[self.channel]
            length = len(tags)
            index = -1
            for i in range(0, length-1):
                if tags[i].x() < x < tags[i + 1].x():  # 找到合适位置
                    index = i
                    break
            if index > -1:
                # 插入
                tag = self.newTag(x, self.contextPos.y())
                tags.insert(index + 1, tag)
                self.calCurveData()
                self.update()
            else:
                print('插入失败')

    def __delTagAction(self):
        if self.contextTag:
            self.tagList[self.channel].remove(self.contextTag)
            self.contextTag.deleteLater()
            self.calCurveData()
            self.update()

    def initTag(self):
        w = self.width()
        h = self.height()
        self.tagList[self.channel].append(self.newTag(0., h))
        self.tagList[self.channel].append(self.newTag(w/2, h/2))
        self.tagList[self.channel].append(self.newTag(w, 0.))
        self.calCurveData()

    def updateSlot(self):
        self.dialog.updateChannelData()

    def changeChanel(self, channel):
        # 隐藏当前显示的tag
        for tag in self.tagList[self.channel]:
            tag.hide()
        # 显示更新后的当前tag
        self.channel = channel
        if len(self.tagList[channel]) == 0:
            self.initTag()
        else:
            for tag in self.tagList[self.channel]:
                tag.show()

    def calCurveData(self):
        tags = self.tagList[self.channel]
        num = len(tags)  # tag数量
        if num <= 1:
            return
        w, h = self.width(), self.height()
        # 绘制 曲线
        spinW = w / 255  # 界面W步进
        spinH = h / 255  # 界面W步进

        anchor = np.zeros((2, num))
        # 转化tag相对屏幕坐标 为灰度级整数坐标
        for i in range(0, num):
            anchor[0, i] = int(tags[i].x2 / spinW)
            anchor[1, i] = int((h - tags[i].y2) / spinH)  # 转化为自然坐标系(向右为x正轴，向上为y正轴)

        # 生成 样条曲线
        _, curveY = CubicSpline.generatorCubicSpline(anchor, 1)
        curveY = np.clip(curveY, 0, 255)
        data = np.asarray(curveY, dtype=np.uint8)
        self.curveDataList[self.channel] = data

    def mouseMoveEvent(self, event: QMouseEvent):
        if self.Active:
            if self.OperatingTag:
                x = event.pos().x()
                y = event.pos().y()
                # 边界检查
                if x > self.width():
                    x = self.width()
                if y > self.height():
                    y = self.height()
                # 相邻锚点检查
                tags = self.tagList[self.channel]
                index = tags.index(self.OperatingTag)
                if 0 < index < len(tags) - 1:
                    if x > tags[index + 1].x2:
                        x = tags[index + 1].x2 - 1
                    elif x < tags[index - 1].x2:
                        x = tags[index - 1].x2 + 1
                elif index == 0:    # 左端点限制
                    x = 0
                elif index == len(tags) - 1:    # 又端点限制
                    x = self.width()

                self.OperatingTag.move2(x, y)
                if self.moveCnt > 6:
                    self.moveCnt = 0
                    self.calCurveData()
                    self.update()
                else:
                    self.moveCnt = self.moveCnt + 1

    def contextMenuEvent(self, event):
        self.context.popup(self.mapToGlobal(event.pos()))
        self.contextPos = event.pos()
        tag = self.childAt(event.x(), event.y())
        self.contextTag = tag
        if tag:
            self.context.newTagAct.setVisible(False)
            self.context.delTagAct.setVisible(True)
        else:
            self.context.delTagAct.setVisible(False)
            self.context.newTagAct.setVisible(True)

    def paintEvent(self, event: QPaintEvent):
        tags = self.tagList[self.channel]
        num = len(tags)  # tag数量
        if num <= 1:
            return
        # 绘图
        curveX = np.float16(np.arange(256) / 255 * self.width())
        curveY = np.float16(self.height() * (1 - self.curveDataList[self.channel] / 255))
        num = curveX.shape[0]  # 点数量
        pen = QPen(self.color[self.channel], 2, Qt.SolidLine)
        qp = QPainter()
        qp.begin(self)
        qp.setPen(pen)
        for i in range(0, num - 1):
            # 画线
            qp.drawLines(QPointF(curveX[i], curveY[i]), QPointF(curveX[i + 1], curveY[i + 1]))
        qp.end()

    def resizeEvent(self, event):
        for tag in self.tagList[self.channel]:
            tag.updatePos()
        self.calCurveData()
        # self.update()
